use std::collections::HashMap;
use bincode::enc::Encoder;
use bincode::{Decode, Encode};
use bincode::de::Decoder;
use bincode::error::{DecodeError, EncodeError};
use bytes::{Buf, BufMut, BytesMut};
use log::info;
use crate::codec::{RpcCodec, RpcCodecFactory, RpcCodecHeader};
use crate::dto::{RpcData, RpcExtData, RpcHeader};
use crate::error::{to_customer_error, RpcError, RpcErrorDto};
use crate::io_util::BytesMutHelp;
use crate::{RpcResult};

#[derive(Default)]
pub struct RpcBinCodeCodec;

impl RpcBinCodeCodec {
    pub fn new() -> RpcBinCodeCodec {
        RpcBinCodeCodec {}
    }
}

impl <T: bincode::Encode + bincode::Decode<()>> RpcCodecFactory<T> for RpcBinCodeCodec {
    fn create() -> Box<dyn RpcCodec<T>> {
        Box::new(RpcBinCodeCodec)
    }
}

impl RpcCodecHeader for RpcBinCodeCodec {

    fn encode_header(&self, header: &RpcHeader) -> RpcResult<Vec<u8>> {
        header.try_into()
    }

    fn decode_header(&self, value: &Vec<u8>) -> RpcResult<(RpcHeader, Vec<u8>)> {
        info!("decode rpc_data_bytes length: {}", value.len());
        let mut buf = BytesMut::with_capacity(value.len());
        buf.put_slice(value);
        let path = BytesMutHelp::read_string(&mut buf)?;
        let version = buf.get_i32();
        let status = buf.get_i32();

        let ext_data_len = buf.get_u32();
        let mut ext_data : HashMap<String, String> = HashMap::with_capacity(ext_data_len as usize);
        for _i in 0..ext_data_len {
            let k = BytesMutHelp::read_string(&mut buf)?;
            let v = BytesMutHelp::read_string(&mut buf)?;
            ext_data.insert(k, v);
        }
        let data = buf.split().freeze().to_vec();

        let header = RpcHeader {
            path,
            version,
            status,
            ext_data: RpcExtData::new(ext_data),
        };
        Ok((header, data))
    }

    fn encode_data(&self, header: &RpcHeader, data: &Vec<u8>) -> RpcResult<Vec<u8>> {
        let header_bytes = self.encode_header(header)?;
        let length = header_bytes.len() + data.len();
        let mut result  = BytesMut::with_capacity(length + 4);
        result.put_u32(length as u32);
        result.extend(header_bytes);
        result.extend(data);
        Ok(result.freeze().to_vec())
    }
}

impl<T: bincode::Encode + bincode::Decode<()>> RpcCodec<T> for RpcBinCodeCodec {

    fn encode(&self, data: &T) -> RpcResult<Vec<u8>> {
        let config = bincode::config::standard()
            // pick one of:
            .with_big_endian()
            .with_little_endian()
            // pick one of:
            .with_variable_int_encoding()
            .with_fixed_int_encoding();

        let bytes = bincode::encode_to_vec(data, config)?;
        Ok(bytes)
    }

    fn decode(&self, data: Vec<u8>) -> RpcResult<T> {
        let config = bincode::config::standard()
            // pick one of:
            .with_big_endian()
            .with_little_endian()
            // pick one of:
            .with_variable_int_encoding()
            .with_fixed_int_encoding();

        let r :(T, usize) = bincode::decode_from_slice(data.as_slice(), config)?;
        Ok(r.0)
    }

    fn encode_rpc_data(&self, data: &RpcData<T>) -> RpcResult<Vec<u8>> {
        let header = self.encode_header(&data.header)?;
        let data = self.encode(&data.data)?;
        let length = header.len() + data.len();
        let mut result  = BytesMut::with_capacity(length + 4);
        result.put_u32(length as u32);
        result.extend(header);
        result.extend(data);
        Ok(result.freeze().to_vec())
    }
}

impl TryInto<Vec<u8>> for &RpcHeader {
    type Error = RpcError;

    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
        let mut bytes_mut = BytesMut::new();
        BytesMutHelp::write_str(&mut bytes_mut, &self.path);

        bytes_mut.put_i32(self.version as i32);
        bytes_mut.put_i32(self.status as i32);

        let len = self.ext_data.ext_data_map.len() as u32;
        bytes_mut.put_u32(len as u32);
        for (key, value) in self.ext_data.ext_data_map.iter() {
            BytesMutHelp::write_str(&mut bytes_mut, key);
            BytesMutHelp::write_str(&mut bytes_mut, value);
        }

        let bytes = bytes_mut.freeze().to_vec();
        // info!("encode rpc_data_bytes length: {}", bytes.len());
        Ok(bytes)
    }
}

impl bincode::Encode for RpcError {
    fn encode<E: Encoder>(
        &self,
        encoder: &mut E,
    ) -> Result<(), EncodeError> {
        let dto = RpcErrorDto::from(self);
        Encode::encode(&dto.code, encoder)?;
        Encode::encode(&dto.message, encoder)?;
        Ok(())
    }
}

impl<Context> bincode::Decode<Context> for RpcError {
    fn decode<D: Decoder<Context = Context>>(
        decoder: &mut D,
    ) -> Result<Self, DecodeError> {
        Ok(to_customer_error(Decode::decode(decoder)?, Decode::decode(decoder)?))
    }
}
impl<'de, Context> bincode::BorrowDecode<'de, Context> for RpcError {
    fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
        decoder: &mut D,
    ) -> Result<Self, DecodeError> {
        Ok(to_customer_error(Decode::decode(decoder)?, Decode::decode(decoder)?))
    }
}